---
title: Java线程的生命周期
---
Java语言里的线程本质上就是操作系统的线程，它们是一一对应的.


## 通用的线程生命周期
通用的线程生命周期基本上可以用下图这个”五态模型“来描述。这五态分别是 ：**初始状态、可运行状态、运行状态、休眠状态和终止状态。**

![9bbc6fa7fb4d631484aa953626cf6ae5](https://img.wkq.pub/pic/9bbc6fa7fb4d631484aa953626cf6ae5.webp)

1. **初始状态**： 线程被创建，但是还不允许分配 CPU 。这个状态属于编程语言特有的，仅仅是编程语言层面被创建，在操作系统层面，真正的线程还没有创建。
2. **可运行状态**： 指的是线程可以分配 CPU 执行。在这种状态下，真正的操作系统线程已经被成功创建了。所以可以分配 CPU 执行。
3. 当有空闲的 CPU 时，操作系统会将其分配给一个处于可运行状态的线程，被分配到 CPU 的线程的状态就转换成了运行状态。
4. 运行状态的线程如果调用一个阻塞的 API (例如以阻塞方式读取文件)或者等待某个事件（例如条件变量），那么线程的状态会转换到休眠状态，同时释放 CPU 使用权，休眠状态的线程永远没有机会获得 CPU 使用权。当等待的事件出现了，线程就会从休眠状态转换到可运行状态。
5. 线程执行完或者出现异常就会进入**终止状态**，终止状态的线程不会切换到其它的任何状态。进入终止状态也就意味着线程的生命周期结束了。


这五种状态在不同的编程语言里会有简化合并。比如，C 语言的 POSIX Threads 规范，就把初始状态和可运行状态合并了；Java 语言里则把可运行状态和运行状态合并了 ，这两个状态在操作系统调度层面有用，而JVM 层面则不关心这两个状态，因为 JVM 把线程调度交给操作系统处理了。

除了简化合并，这五种状态也有可能被细化，比如，Java 语言里就细化了休眠状态。

## Java 中线程的生命周期

Java 语言中的线程生命周期共有六种状态，分别是：

1. NEW (初始状态)
2. RUNNABLE (可运行/运行状态)
3. BLOCKED (阻塞状态)
4. WAITING (有时限等待)
5. TIMED_WAITING (无时限等待)
6. TERMINATED (终止状态)

在操作系统层面，BLOCKED、WAITING、TIMED_WAITING 是一种状态。也就是说线程只要处于这三种状态之一，就永远没有机会获得 CPU 的使用权。

Java 线程的生命周期可以简化为下图：



![3f6c6bf95a6e8627bdf3cb621bbb7f8c](https://img.wkq.pub/pic/3f6c6bf95a6e8627bdf3cb621bbb7f8c.webp)



其中 BLOCKED、WAITING、TIMED_WAITING 可以理解为导致线程休眠的三种原因。

### 1. RUNNABLE 与 BLOCK 的状态转换

只有一种场景会触发这种转换，就是线程等待 synchronized 隐式锁。synchronzied 修饰的方法、代码块同一时刻只允许一个线程执行，其它线程只能等待。这种情况下，等待的线程就会从 RUNNABLE  转换到 BLOCKED 状态，而当等待的线程获得 synchronized 隐式锁时，就又会从 BLOCKED 状态转换到 RUNNABLE 状态。



当线程调用阻塞式 API 时，在操作系统层面，线程是会转换到休眠状态的，但是在  JVM 层面， Java 线程的状态并不会发生变化。Java 线程仍然保持 RUNNABLE 状态。**JVM 层面不关心操作系统调度相关的状态**，因为在 JVM 看来，等待 CPU 使用权（操作系统层面此时处于可执行状态）与等待 I/O（操作系统层面处于休眠状态）没有区别。都是等待某个资源，所以都归入了 RUNNABLE 状态。

:::tip 阻塞式API

API 会阻塞应用程序的执行，直到 API 完成其操作并返回结果。

:::

所谓的 Java 在调用阻塞式 API 时，线程会阻塞，指的是操作系统线程的状态，并不是 Java 线程的状态。

### 2. RUNNABLE 和 WAITING 的状态转换

总体来说，会有三种场景会触发这种转换。

第一种，获得 synchonrized 隐式锁的线程，调用 锁对象的无参数的 wait() 方法。

第二种，调用无参数的 Thread.join () 方法，当调用 A.join() 的时候，执行这条语句的线程会等待 theadA 执行完，等待中的这个线程，其状态会从 RUNNABLE 变为 WAITING。当线程 threadA 执行完，原来等待它的线程又会从 WAITING 状态变为 RUNNABLE 状态。

第三种，调用 LockSupport.park() 方法，调用 LockSupport.park() 方法，当前线程会阻塞，线程的状态会从 RUNNABLE 转换到 WAITING 。调用 LockSupport.unpark(Thread thread) 可唤醒目标线程，目标线程的状态从 WAITING 变为 RUNNABLE。

### 3. RUNNABLE 和 TIMED_WAITING 的状态转换

有五种场景会触发这种转换： 

1. 带超时参数的 Thread.sleep(long mills) ；方法
2. 获得 synchronized 隐式锁的线程，调用带超时参数的 Object.wait(long timeout)  方法。
3. 调用带超时参数的 Thead.join(long millis) 方法；
4. 调用带超时参数的 LockSupport.parkNanos(Object blocker,long deadline);
5. 调用带超时参数的 LockSupport.parkUtil(long deadline)方法；

### 4. 从 NEW 到 RUNNABLE 状态

Java 刚创建出来的 Thread 对象就是 NEW 状态，NEW 状态的线程，不会被操作系统调度。Java 线程要执行，就必须转换到 RUNNABLE 状态，只需要调用线程的 start() 方法就可以了。

### 5. 从 RUNNABLE 到 TERMIATED 状态

线程执行完 run() 方法后，会自动切换到 TERMIATED 状态；如果执行 run() 方法的时候抛出异常，也会导致线程终止。有时候需要强制中断 run() 方法的执行，可以调用 interrupt() 方法。

interrupt() 方法仅仅是通知线程，线程有机会执行一些后续操作，同时也可以无视这个通知。

被 interrupt 的线程，是怎么收到通知的呢？一种是异常，另一种是主动检测。

当线程 A 处于 WAITING 、TIMED_WAITING 状态时，如果其它线程调用线程 A 的 interrupt 方法，会使线程 A 返回  RUNNABLE 状态，同时线程  A 的代码会触发 InterruptedException 异常。

当线程 A 处于 RUNNABLE 状态时，并且阻塞在 java.nio.channels.InterruptibleChannel 上时，如果其它线程调用线程 A 的 interrupt() 方法，线程 A 会触发 java.nio.channels.ClosedByInterruptException 这个异常；而阻塞在 java.nio.channels.Selector 上时，如果其他线程调用线程 A 的 interrupt() 方法，线程 A 的 java.nio.channels.Selector 会立即返回。



上面两种情况属于被中断的线程通过异常的方式获得了通知。还有一种是主动检测，被中断线程可以通过 isInterrupted() 方法，检测是不是自己被中断了。

抛出 InterruptedException 异常后，会清除当前线程的中断标识。



:::tip

线程处于阻塞状态不响应中断，JUC 并发包里的锁有方法能响应中断。

:::



